//---------------------------------------------------------------------
//THIS CODE AND INFORMATION ARE PROVIDED AS IS WITHOUT WARRANTY OF ANY
//KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
//IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
//PARTICULAR PURPOSE.
//---------------------------------------------------------------------

namespace Microsoft.Samples.Alert {

    using System;
    using System.Web;
    using System.Web.UI;
    using System.Web.UI.WebControls;
    using System.Collections;
    using System.ComponentModel;
    using System.Drawing;
    using System.Drawing.Design;
    using System.Globalization;
    using System.Security.Permissions;

    /// <summary>
    /// The alert class makes it easy to display rich client-side message boxes using server code.
    /// </summary>
    [
    ToolboxData("<{0}:Alert runat=\"server\"></{0}:Alert>"),
    ParseChildren(false),
    PersistChildren(true),
    AspNetHostingPermission(SecurityAction.LinkDemand, Level = AspNetHostingPermissionLevel.Minimal),
    AspNetHostingPermission(SecurityAction.InheritanceDemand, Level = AspNetHostingPermissionLevel.Minimal)
    ]
    public class Alert : WebControl, IPostBackEventHandler {

        private static readonly object EventChoice = new object();

        // The two parts of the script that will display the Alert.
        // The middle part will be the url of the DisplayAlert.htm page that will
        // render the modal dialog from the parameters it's being passed.
        // The script degrades to an alert or a confirm on non-IE browsers.
        // The getInnerText extracts the text from the HTML of the Alert in order to
        // display it on browsers that can't show modal dialogs.
        private readonly string _script1 = @"
function messageBoxShow(msgBoxID, buttons) {
	var msgBox = (document.all ? document.all[msgBoxID] : document.getElementById(msgBoxID));
	if (window.showModalDialog) {
		var parameters = new Array(2);
		parameters[0] = msgBox.title;
		parameters[1] = msgBox.outerHTML;
		return window.showModalDialog('";

        private readonly string _script2 = @"', parameters, 'resizable:yes;scroll:no;status:no;help:no;dialogHeight:' + msgBox.style.height + ';dialogWidth:' + msgBox.style.width + ';center:yes');
	}
	else {
		var txt = getInnerText(msgBox);
		if (buttons) {
			return confirm(txt);
		}
		else {
			alert(txt);
			return true;
		}
	}
}
function getInnerText(element) {
	var innerText = """";
	if (element.nodeType == 3 && element.nodeValue) {
		innerText = element.nodeValue;
	}
	var children = element.childNodes;
	if (children.length > 0) {
		for(var i = 0; i < children.length; i++) {
			innerText += getInnerText(children[i]);
		}
	}
	return innerText;
}
";

        public Alert() : base(HtmlTextWriterTag.Div) {
        }

        /// <summary>
        /// The text of the Abort button.
        /// </summary>
        [
        Category("ButtonText"),
        DefaultValue("Abort"),
        Description("The text of the Abort button.")
        ]
        public virtual string AbortText {
            get {
                string s = (string)ViewState["AbortText"];
                return ((s == null) ? "Abort" : s);
            }
            set {
                ViewState["AbortText"] = value;
            }
        }

        /// <summary>
        /// The url of the background image.
        /// </summary>
        [
        Category("Appearance"),
        DefaultValue(""),
        Description("The url of the background image."),
        UrlProperty()
        ]
        public virtual string BackImageUrl {
            get {
                string s = (string)ViewState["BackImageUrl"];
                return ((s == null) ? String.Empty : s);
            }
            set {
                ViewState["BackImageUrl"] = value;
            }
        }

        /// <summary>
        /// The number and nature of the buttons to use for the alert.
        /// </summary>
        [
        Category("Appearance"),
        DefaultValue(AlertButtons.OK),
        Description("The number and nature of the buttons to use for the alert.")
        ]
        public virtual AlertButtons Buttons {
            get {
                object b = ViewState["Buttons"];
                return ((b == null) ? AlertButtons.OK : (AlertButtons)b);
            }
            set {
                ViewState["Buttons"] = value;
            }
        }

        /// <summary>
        /// The text of the Cancel button.
        /// </summary>
        [
        Category("ButtonText"),
        DefaultValue("Cancel"),
        Description("The text of the Cancel button.")
        ]
        public virtual string CancelText {
            get {
                string s = (string)ViewState["CancelText"];
                return ((s == null) ? "Cancel" : s);
            }
            set {
                ViewState["CancelText"] = value;
            }
        }

        /// <summary>
        /// The direction of the text in the Alert box.
        /// </summary>
        [
        Category("Layout"),
        DefaultValue(ContentDirection.NotSet),
        Description("The direction of the text in the Alert box.")
        ]
        public virtual ContentDirection Direction {
            get {
                Object direction = ViewState["Direction"];
                return direction == null ? ContentDirection.NotSet : (ContentDirection)direction;
            }
            set {
                ViewState["Direction"] = value;
            }
        }

        /// <summary>
        /// The horizontal alignement of the contents of the Alert.
        /// </summary>
        [
        Category("Layout"),
        DefaultValue(HorizontalAlign.NotSet),
        Description("The horizontal alignement of the contents of the Alert.")
        ]
        public virtual HorizontalAlign HorizontalAlign {
            get {
                object o = ViewState["HorizontalAlign"];
                return ((o == null) ? HorizontalAlign.NotSet : (HorizontalAlign)o);
            }
            set {
                if (value < HorizontalAlign.NotSet || value > HorizontalAlign.Justify) {
                    throw new ArgumentOutOfRangeException("value");
                }
                ViewState["HorizontalAlign"] = value;
            }
        }

        /// <summary>
        /// The text of the Ignore button.
        /// </summary>
        [
        Category("ButtonText"),
        DefaultValue("Ignore"),
        Description("The text of the Ignore button.")
        ]
        public virtual string IgnoreText {
            get {
                string s = (string)ViewState["IgnoreText"];
                return ((s == null) ? "Ignore" : s);
            }
            set {
                ViewState["IgnoreText"] = value;
            }
        }

        /// <summary>
        /// The text of the No button.
        /// </summary>
        [
        Category("ButtonText"),
        DefaultValue("No"),
        Description("The text of the No button.")
        ]
        public virtual string NoText {
            get {
                string s = (string)ViewState["NoText"];
                return ((s == null) ? "No" : s);
            }
            set {
                ViewState["NoText"] = value;
            }
        }

        /// <summary>
        /// The text of the OK button.
        /// </summary>
        [
        Category("ButtonText"),
        DefaultValue("OK"),
        Description("The text of the OK button.")
        ]
        public virtual string OKText {
            get {
                string s = (string)ViewState["OKText"];
                return ((s == null) ? "OK" : s);
            }
            set {
                ViewState["OKText"] = value;
            }
        }

        /// <summary>
        /// The text of the Retry button.
        /// </summary>
        [
        Category("ButtonText"),
        DefaultValue("Retry"),
        Description("The text of the Retry button.")
        ]
        public virtual string RetryText {
            get {
                string s = (string)ViewState["RetryText"];
                return ((s == null) ? "Retry" : s);
            }
            set {
                ViewState["RetryText"] = value;
            }
        }

        /// <summary>
        /// Determines what scroll bars can appear on the Alert.
        /// </summary>
        [
        Category("Layout"),
        DefaultValue(ScrollBars.None),
        Description("Determines what scroll bars can appear on the Alert.")
        ]
        public virtual ScrollBars ScrollBars {
            get {
                object scroll = ViewState["ScrollBars"];
                return ((scroll == null) ? ScrollBars.None : (ScrollBars)scroll);
            }
            set {
                ViewState["ScrollBars"] = value;
            }
        }

        /// <summary>
        /// The title of the alert.
        /// </summary>
        [
        Category("Layout"),
        DefaultValue(""),
        Description("The title of the alert.")
        ]
        public virtual string Title {
            get {
                object title = ViewState["Title"];
                return ((title == null) ? string.Empty : (string)title);
            }
            set {
                ViewState["Title"] = value;
            }
        }

        /// <summary>
        /// Set to false if the text in the Alert should not be allowed to wrap.
        /// </summary>
        [
        Category("Layout"),
        DefaultValue(true),
        Description("Set to false if the text in the Alert should not be allowed to wrap.")
        ]
        public virtual bool Wrap {
            get {
                object b = ViewState["Wrap"];
                return ((b == null) ? true : (bool)b);
            }
            set {
                ViewState["Wrap"] = value;
            }
        }

        /// <summary>
        /// The text of the Yes button.
        /// </summary>
        [
        Category("ButtonText"),
        DefaultValue("Yes"),
        Description("The text of the Yes button.")
        ]
        public virtual string YesText {
            get {
                string s = (string)ViewState["YesText"];
                return ((s == null) ? "Yes" : s);
            }
            set {
                ViewState["YesText"] = value;
            }
        }

        /// <summary>
        /// The event that is raised when an option is clicked in the Alert.
        /// </summary>
        public event AlertChoiceEventHandler Choice {
            add {
                Events.AddHandler(EventChoice, value);
            }
            remove {
                Events.RemoveHandler(EventChoice, value);
            }
        }

        protected override void AddAttributesToRender(HtmlTextWriter writer) {
            base.AddAttributesToRender(writer);

            string s = BackImageUrl;
            if (s.Trim().Length > 0) {
                writer.AddStyleAttribute(HtmlTextWriterStyle.BackgroundImage, "url(" + ResolveClientUrl(s) + ")");
            }

			switch (ScrollBars) {
				case ScrollBars.Horizontal:
					writer.AddStyleAttribute(HtmlTextWriterStyle.OverflowX, "scroll");
					break;
				case ScrollBars.Vertical:
					writer.AddStyleAttribute(HtmlTextWriterStyle.OverflowY, "scroll");
					break;
				case ScrollBars.Both:
					writer.AddStyleAttribute(HtmlTextWriterStyle.Overflow, "scroll");
					break;
				case ScrollBars.Auto:
					writer.AddStyleAttribute(HtmlTextWriterStyle.Overflow, "auto");
					break;
				default:
					break;
			}

            HorizontalAlign hAlign = HorizontalAlign;
            if (hAlign != HorizontalAlign.NotSet) {
                TypeConverter hac = TypeDescriptor.GetConverter(typeof(HorizontalAlign));
                writer.AddAttribute(HtmlTextWriterAttribute.Align, hac.ConvertToString(hAlign).ToLower(CultureInfo.InvariantCulture));
            }

            if (!Wrap)
                writer.AddStyleAttribute(HtmlTextWriterStyle.WhiteSpace, "nowrap");

            if (Direction == ContentDirection.LeftToRight) {
                writer.AddAttribute(HtmlTextWriterAttribute.Dir, "LTR");
            }
            else if (Direction == ContentDirection.RightToLeft) {
                writer.AddAttribute(HtmlTextWriterAttribute.Dir, "RTL");
            }

            // Position
            writer.AddStyleAttribute("display", "none");
            // Title
            writer.AddAttribute("title", Title);
        }

        /// <summary>
        /// Get a client-side function that will show the Alert.
        /// </summary>
        /// <returns>The client-side code that will show the Alert</returns>
        public string GetShowClientEvent() {
            return "messageBoxShow('" + ClientID + "', " + ((Buttons == AlertButtons.OK) ? "false" : "true") + ")";
        }

        /// <summary>
        /// This function is called when an option is chosen in the Alert.
        /// </summary>
        /// <param name="e">The event arguments that contain the chosen option.</param>
        protected virtual void OnChoice(AlertChoiceEventArgs e) {
            AlertChoiceEventHandler handler = (AlertChoiceEventHandler)Events[EventChoice];
            if (handler != null) handler(this, e);
        }

		protected override void OnPreRender(EventArgs e) {
            // Register the Alert display script
			Page.ClientScript.RegisterClientScriptBlock(
				typeof(Alert),
				"AspNetAlert",
				(_script1 +
				Page.ResolveClientUrl("~/Alert/DisplayAlert.htm") +
				_script2),
				true);
		}

		protected override void RenderChildren(HtmlTextWriter writer) {
            // First, render the contents of the Alert
			base.RenderChildren(writer);
            // Then, we prepare the button panel and its three buttons
			Panel p = new Panel();
			p.ID = UniqueID + "_Buttons";
			p.Page = Page;
			Button button1 = new Button();
			button1.OnClientClick = "returnValue=true;window.close();";
			p.Controls.Add(button1);
			Button button2 = new Button();
			button2.OnClientClick = "returnValue=false;window.close();";
			p.Controls.Add(button2);
			Button button3 = new Button();
			button3.OnClientClick = "window.close();";
			p.Controls.Add(button3);
            // Set-up and show or hide the buttons according to the button style
            switch (Buttons) {
                case AlertButtons.AbortRetryIgnore:
                    button1.Visible = true;
                    button2.Visible = true;
                    button3.Visible = true;
                    button1.Text = AbortText;
                    button2.Text = RetryText;
                    button3.Text = IgnoreText;
                    break;
                case AlertButtons.OK:
                    button1.Visible = true;
                    button2.Visible = false;
                    button3.Visible = false;
                    button1.Text = OKText;
                    break;
                case AlertButtons.OKCancel:
                    button1.Visible = true;
                    button2.Visible = true;
                    button3.Visible = false;
                    button1.Text = OKText;
                    button2.Text = CancelText;
                    break;
                case AlertButtons.RetryCancel:
                    button1.Visible = false;
                    button2.Visible = true;
                    button3.Visible = true;
                    button2.Text = RetryText;
                    button3.Text = CancelText;
                    break;
                case AlertButtons.YesNo:
                    button1.Visible = true;
                    button2.Visible = true;
                    button3.Visible = false;
                    button1.Text = YesText;
                    button2.Text = NoText;
                    break;
                case AlertButtons.YesNoCancel:
                    button1.Visible = true;
                    button2.Visible = true;
                    button3.Visible = true;
                    button1.Text = YesText;
                    button2.Text = NoText;
                    button3.Text = CancelText;
					break;
			}
            // Render the button panel
			p.RenderControl(writer);
		}

        /// <summary>
        /// Call this server-side method to ask the client to show the Alert on this request.
        /// </summary>
        public void Show() {
            // Force the page to output the postback script
            Page.ClientScript.GetPostBackEventReference(this, String.Empty);
            // We can't use GetPostBackEvent here because the argument is not a string literal, but a client variable.
            Page.ClientScript.RegisterStartupScript(typeof(Alert), "ServerShowAlert",
                "var res=" + GetShowClientEvent() + ";__doPostBack('" + UniqueID + "',res);", true);
        }

        void IPostBackEventHandler.RaisePostBackEvent(string eventArgument) {
            OnChoice(new AlertChoiceEventArgs(Buttons, eventArgument));
        }
    }

    public delegate void AlertChoiceEventHandler(object source, AlertChoiceEventArgs e);

}
